﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Net.Mail;
using System.Activities;
using System.Data.Entity;

using ERPStore.Extensions;
using ERPStore.NewsLetters.Repositories;
using ERPStore.NewsLetters.Models;

using CloudSoft.Extensions;

namespace ERPStore.NewsLetters.Services
{
	/// <summary>
	/// Service des news letters
	/// </summary>
	public class NewsLettersService
	{
		public NewsLettersService(
            ERPStore.Logging.ILogger logger
			, ERPStore.Services.ICacheService cacheService
			, ERPStore.Services.IAccountService accountService
            , ERPStore.Services.IScheduledTaskService taskService
			, ERPStore.NewsLetters.Models.Settings settings
			, ERPStore.Services.IValidatorService validatorService
			, CloudSoft.Repositories.IRepository<NewsLettersDbContext> sqlRepository
			// , ERPStore.Services.IServiceBus serviceBus
			)
		{
			this.Logger = logger;
			this.AccountService = accountService;
            this.TaskService = taskService;
			this.Settings = settings;
			this.ValidatorService = validatorService;
			this.DbRepository = sqlRepository;
			this.CacheService = cacheService;
			// this.ServiceBus = serviceBus;
		}

		protected CloudSoft.Repositories.IRepository<NewsLettersDbContext> DbRepository { get; private set; }
		protected ERPStore.Logging.ILogger Logger { get; private set; }
		protected ERPStore.Services.IAccountService AccountService { get; private set; }
        protected ERPStore.Services.IScheduledTaskService TaskService { get; private set; }
		protected ERPStore.NewsLetters.Models.Settings Settings { get; private set; }
		internal ERPStore.Services.IValidatorService ValidatorService { get; private set; }
		protected ERPStore.Services.ICacheService CacheService { get; private set; }
		// protected ERPStore.Services.IServiceBus ServiceBus { get; private set; }

		#region MimetypeList

		public static Dictionary<string, string> MIMETypeDictionary
			= new Dictionary<string, string>()
		{
   {"323", "text/h323"},
   {"3gp", "video/3gpp"},
   {"3gpp", "video/3gpp"},
   {"acp", "audio/x-mei-aac"},
   {"act", "text/xml"},
   {"actproj", "text/plain"},
   {"ade", "application/msaccess"},
   {"adp", "application/msaccess"},
   {"ai", "application/postscript"},
   {"aif", "audio/aiff"},
   {"aifc", "audio/aiff"},
   {"aiff", "audio/aiff"},
   {"asf", "video/x-ms-asf"},
   {"asm", "text/plain"},
   {"asx", "video/x-ms-asf"},
   {"au", "audio/basic"},
   {"avi", "video/avi"},
   {"bmp", "image/bmp"},
   {"bwp", "application/x-bwpreview"},
   {"c", "text/plain"},
   {"cat", "application/vnd.ms-pki.seccat"},
   {"cc", "text/plain"},
   {"cdf", "application/x-cdf"},
   {"cer", "application/x-x509-ca-cert"},
   {"cod", "text/plain"},
   {"cpp", "text/plain"},
   {"crl", "application/pkix-crl"},
   {"crt", "application/x-x509-ca-cert"},
   {"cs", "text/plain"},
   {"css", "text/css"},
   {"csv", "application/vnd.ms-excel"},
   {"cxx", "text/plain"},
   {"dbs", "text/plain"},
   {"def", "text/plain"},
   {"der", "application/x-x509-ca-cert"},
   {"dib", "image/bmp"},
   {"dif", "video/x-dv"},
   {"dll", "application/x-msdownload"},
   {"doc", "application/msword"},
   {"dot", "application/msword"},
   {"dsp", "text/plain"},
   {"dsw", "text/plain"},
   {"dv", "video/x-dv"},
   {"edn", "application/vnd.adobe.edn"},
   {"eml", "message/rfc822"},
   {"eps", "application/postscript"},
   {"etd", "application/x-ebx"},
   {"etp", "text/plain"},
   {"exe", "application/x-msdownload"},
   {"ext", "text/plain"},
   {"fdf", "application/vnd.fdf"},
   {"fif", "application/fractals"},
   {"fky", "text/plain"},
   {"gif", "image/gif"},
   {"gz", "application/x-gzip"},
   {"h", "text/plain"},
   {"hpp", "text/plain"},
   {"hqx", "application/mac-binhex40"},
   {"hta", "application/hta"},
   {"htc", "text/x-component"},
   {"htm", "text/html"},
   {"html", "text/html"},
   {"htt", "text/webviewhtml"},
   {"hxx", "text/plain"},
   {"i", "text/plain"},
   {"iad", "application/x-iad"},
   {"ico", "image/x-icon"},
   {"ics", "text/calendar"},
   {"idl", "text/plain"},
   {"iii", "application/x-iphone"},
   {"inc", "text/plain"},
   {"infopathxml", "application/ms-infopath.xml"},
   {"inl", "text/plain"},
   {"ins", "application/x-internet-signup"},
   {"iqy", "text/x-ms-iqy"},
   {"isp", "application/x-internet-signup"},
   {"java", "text/java"},
   {"jnlp", "application/x-java-jnlp-file"},
   {"jpg", "image/jpeg"},
   {"jpe", "image/jpeg"},
   {"jpeg", "image/jpeg"},
   {"jfif", "image/jpeg"},
   {"jsl", "text/plain"},
   {"kci", "text/plain"},
   {"la1", "audio/x-liquid-file"},
   {"lar", "application/x-laplayer-reg"},
   {"latex", "application/x-latex"},
   {"lavs", "audio/x-liquid-secure"},
   {"lgn", "text/plain"},
   {"lmsff", "audio/x-la-lms"},
   {"lqt", "audio/x-la-lqt"},
   {"lst", "text/plain"},
   {"m1v", "video/mpeg"},
   {"m3u", "audio/mpegurl"},
   {"m4e", "video/mpeg4"},
   {"MAC", "image/x-macpaint"},
   {"mak", "text/plain"},
   {"man", "application/x-troff-man"},
   {"map", "text/plain"},
   {"mda", "application/msaccess"},
   {"mdb", "application/msaccess"},
   {"mde", "application/msaccess"},
   {"mdi", "image/vnd.ms-modi"},
   {"mfp", "application/x-shockwave-flash"},
   {"mht", "message/rfc822"},
   {"mhtml", "message/rfc822"},
   {"mid", "audio/mid"},
   {"midi", "audio/mid"},
   {"mk", "text/plain"},
   {"mnd", "audio/x-musicnet-download"},
   {"mns", "audio/x-musicnet-stream"},
   {"MP1", "audio/mp1"},
   {"mp2", "video/mpeg"},
   {"mp2v", "video/mpeg"},
   {"mp3", "audio/mpeg"},
   {"mp4", "video/mp4"},
   {"mpa", "video/mpeg"},
   {"mpe", "video/mpeg"},
   {"mpeg", "video/mpeg"},
   {"mpf", "application/vnd.ms-mediapackage"},
   {"mpg", "video/mpeg"},
   {"mpg4", "video/mp4"},
   {"mpga", "audio/rn-mpeg"},
   {"mpv2", "video/mpeg"},
   {"NMW", "application/nmwb"},
   {"nws", "message/rfc822"},
   {"odc", "text/x-ms-odc"},
   {"odh", "text/plain"},
   {"odl", "text/plain"},
   {"p10", "application/pkcs10"},
   {"p12", "application/x-pkcs12"},
   {"p7b", "application/x-pkcs7-certificates"},
   {"p7c", "application/pkcs7-mime"},
   {"p7m", "application/pkcs7-mime"},
   {"p7r", "application/x-pkcs7-certreqresp"},
   {"p7s", "application/pkcs7-signature"},
   {"PCT", "image/pict"},
   {"pdf", "application/pdf"},
   {"pdx", "application/vnd.adobe.pdx"},
   {"pfx", "application/x-pkcs12"},
   {"pic", "image/pict"},
   {"PICT", "image/pict"},
   {"pko", "application/vnd.ms-pki.pko"},
   {"png", "image/png"},
   {"pnt", "image/x-macpaint"},
   {"pntg", "image/x-macpaint"},
   {"pot", "application/vnd.ms-powerpoint"},
   {"ppa", "application/vnd.ms-powerpoint"},
   {"pps", "application/vnd.ms-powerpoint"},
   {"ppt", "application/vnd.ms-powerpoint"},
   {"prc", "text/plain"},
   {"prf", "application/pics-rules"},
   {"ps", "application/postscript"},
   {"pub", "application/vnd.ms-publisher"},
   {"pwz", "application/vnd.ms-powerpoint"},
   {"qt", "video/quicktime"},
   {"qti", "image/x-quicktime"},
   {"qtif", "image/x-quicktime"},
   {"qtl", "application/x-quicktimeplayer"},
   {"qup", "application/x-quicktimeupdater"},
   {"r1m", "application/vnd.rn-recording"},
   {"r3t", "text/vnd.rn-realtext3d"},
   {"RA", "audio/vnd.rn-realaudio"},
   {"RAM", "audio/x-pn-realaudio"},
   {"rat", "application/rat-file"},
   {"rc", "text/plain"},
   {"rc2", "text/plain"},
   {"rct", "text/plain"},
   {"rec", "application/vnd.rn-recording"},
   {"rgs", "text/plain"},
   {"rjs", "application/vnd.rn-realsystem-rjs"},
   {"rjt", "application/vnd.rn-realsystem-rjt"},
   {"RM", "application/vnd.rn-realmedia"},
   {"rmf", "application/vnd.adobe.rmf"},
   {"rmi", "audio/mid"},
   {"RMJ", "application/vnd.rn-realsystem-rmj"},
   {"RMM", "audio/x-pn-realaudio"},
   {"rms", "application/vnd.rn-realmedia-secure"},
   {"rmvb", "application/vnd.rn-realmedia-vbr"},
   {"RMX", "application/vnd.rn-realsystem-rmx"},
   {"RNX", "application/vnd.rn-realplayer"},
   {"rp", "image/vnd.rn-realpix"},
   {"RPM", "audio/x-pn-realaudio-plugin"},
   {"rqy", "text/x-ms-rqy"},
   {"rsml", "application/vnd.rn-rsml"},
   {"rt", "text/vnd.rn-realtext"},
   {"rtf", "application/msword"},
   {"rul", "text/plain"},
   {"RV", "video/vnd.rn-realvideo"},
   {"s", "text/plain"},
   {"sc2", "application/schdpl32"},
   {"scd", "application/schdpl32"},
   {"sch", "application/schdpl32"},
   {"sct", "text/scriptlet"},
   {"sd2", "audio/x-sd2"},
   {"sdp", "application/sdp"},
   {"sit", "application/x-stuffit"},
   {"slk", "application/vnd.ms-excel"},
   {"sln", "application/octet-stream"},
   {"SMI", "application/smil"},
   {"smil", "application/smil"},
   {"snd", "audio/basic"},
   {"snp", "application/msaccess"},
   {"spc", "application/x-pkcs7-certificates"},
   {"spl", "application/futuresplash"},
   {"sql", "text/plain"},
   {"srf", "text/plain"},
   {"ssm", "application/streamingmedia"},
   {"sst", "application/vnd.ms-pki.certstore"},
   {"stl", "application/vnd.ms-pki.stl"},
   {"swf", "application/x-shockwave-flash"},
   {"tab", "text/plain"},
   {"tar", "application/x-tar"},
   {"tdl", "text/xml"},
   {"tgz", "application/x-compressed"},
   {"tif", "image/tiff"},
   {"tiff", "image/tiff"},
   {"tlh", "text/plain"},
   {"tli", "text/plain"},
   {"torrent", "application/x-bittorrent"},
   {"trg", "text/plain"},
   {"txt", "text/plain"},
   {"udf", "text/plain"},
   {"udt", "text/plain"},
   {"uls", "text/iuls"},
   {"user", "text/plain"},
   {"usr", "text/plain"},
   {"vb", "text/plain"},
   {"vcf", "text/x-vcard"},
   {"vcproj", "text/plain"},
   {"viw", "text/plain"},
   {"vpg", "application/x-vpeg005"},
   {"vspscc", "text/plain"},
   {"vsscc", "text/plain"},
   {"vssscc", "text/plain"},
   {"wav", "audio/wav"},
   {"wax", "audio/x-ms-wax"},
   {"wbk", "application/msword"},
   {"wiz", "application/msword"},
   {"wm", "video/x-ms-wm"},
   {"wma", "audio/x-ms-wma"},
   {"wmd", "application/x-ms-wmd"},
   {"wmv", "video/x-ms-wmv"},
   {"wmx", "video/x-ms-wmx"},
   {"wmz", "application/x-ms-wmz"},
   {"wpl", "application/vnd.ms-wpl"},
   {"wprj", "application/webzip"},
   {"wsc", "text/scriptlet"},
   {"wvx", "video/x-ms-wvx"},
   {"XBM", "image/x-xbitmap"},
   {"xdp", "application/vnd.adobe.xdp+xml"},
   {"xfd", "application/vnd.adobe.xfd+xml"},
   {"xfdf", "application/vnd.adobe.xfdf"},
   {"xla", "application/vnd.ms-excel"},
   {"xlb", "application/vnd.ms-excel"},
   {"xlc", "application/vnd.ms-excel"},
   {"xld", "application/vnd.ms-excel"},
   {"xlk", "application/vnd.ms-excel"},
   {"xll", "application/vnd.ms-excel"},
   {"xlm", "application/vnd.ms-excel"},
   {"xls", "application/vnd.ms-excel"},
   {"xlt", "application/vnd.ms-excel"},
   {"xlv", "application/vnd.ms-excel"},
   {"xlw", "application/vnd.ms-excel"},
   {"xml", "text/xml"},
   {"xpl", "audio/scpls"},
   {"xsl", "text/xml"},
   {"z", "application/x-compress"},
   {"zip", "application/x-zip-compressed"}
  };

		#endregion

		#region Newsletter

		public Models.NewsLetter GetNewsLetterById(int id)
		{
			var key = string.Format("NewsLetter.NewsLetter.{0}", id);
			var result = CacheService[key] as Models.NewsLetter;
			if (result == null)
			{
				var data = DbRepository.Get<Repositories.Datas.NewsLetterData>(i => i.Id == id);
				result = AutoMapper.Mapper.Map<Models.NewsLetter>(data);
				PrepareNewsletter(result);
				CacheService.Add(key, result, DateTime.Now.AddHours(1));
			}
			return result;
		}

		internal Models.NewsLetter GetNewsLetterByFileName(string fileName)
		{
			var data = DbRepository.Get<Repositories.Datas.NewsLetterData>(i => i.FileName == fileName);
			var result = AutoMapper.Mapper.Map<Models.NewsLetter>(data);
			PrepareNewsletter(result);
			return result;
		}

		public Models.NewsLetter CreateNewsLetter()
		{
			var result = new Models.NewsLetter();
			var query = DbRepository.Query<Repositories.Datas.NewsLetterData>();
			var maxId = query.Count() > 0 ? query.Max(i => i.Id) : 0;
			result.FileName = string.Format("NL{0:000000}.cshtml", maxId + 1);
			PrepareNewsletter(result);
			return result;
		}

		public Models.NewsLetter DuplicateNewsLetter(Models.NewsLetter original)
		{
			var result = CreateNewsLetter();
			result.Content = original.Content;
			return result;
		}

		private void PrepareNewsletter(Models.NewsLetter model)
		{
			if (model == null)
			{
				return;
			}
			model.CreationDate = DateTime.Now;
			var virtualFilePath = ERPStore.NewsLetters.Configuration.ConfigurationSettings.AppSettings["virtualFilePath"];
			model.ContentViewName = virtualFilePath + "/" + model.FileName;
			model.Title = FindTitle(model.Content);
		}

		private string FindTitle(string htmlContent)
		{
			if (htmlContent.IsNullOrEmpty())
			{
				return "sans titre";
			}
			var match = System.Text.RegularExpressions.Regex.Match(htmlContent, @"<title>(?<t>[^<]*)</title>");
			if (match.Success)
			{
				return match.Groups["t"].Value;
			}
			return "sans titre";
		}

		public IList<ERPStore.Models.BrokenRule> ValidateNewsLetter(Models.NewsLetter model)
		{
			var br = ValidatorService.Validate(model);
			return br;
		}

		public void SaveNewsLetter(Models.NewsLetter model)
		{
			var br = ValidateNewsLetter(model);
			if (!br.IsNullOrEmpty())
			{
				throw new ERPStore.Models.ValidationException(br);
			}

			model.LastUpdate = DateTime.Now;
			Repositories.Datas.NewsLetterData data = null;
			if (model.Id == 0)
			{
				data = AutoMapper.Mapper.Map<Repositories.Datas.NewsLetterData>(model);
				DbRepository.Insert<Repositories.Datas.NewsLetterData>(data);
			}
			else
			{
				data = DbRepository.Get<Repositories.Datas.NewsLetterData>(i => i.Id == model.Id);
				if (data == null)
				{
					throw new System.Data.RowNotInTableException();
				}
				if (!data.Version.SequenceEqual(model.Version))
				{
					throw new System.Data.DBConcurrencyException();
				}
				if (data.FileName != model.FileName)
				{
					var oldFileName = System.IO.Path.Combine(GetPhysicalPath(), data.FileName);
					var newFileName = System.IO.Path.Combine(GetPhysicalPath(), model.FileName);
					var backUpFileName = System.IO.Path.Combine(GetPhysicalPath(), string.Format("{0}.{1}.bak", oldFileName, Guid.NewGuid()));
					if (System.IO.File.Exists(oldFileName))
					{
						try
						{
							System.IO.File.Copy(oldFileName, backUpFileName);
							System.IO.File.Delete(oldFileName);
						}
						catch(Exception ex)
						{
							// Mort pour la science
							Logger.Error(ex);
						}
					}
				}
				data = AutoMapper.Mapper.Map<Models.NewsLetter, Repositories.Datas.NewsLetterData>(model, data);
				DbRepository.Update<Repositories.Datas.NewsLetterData>(data);
			}

			model.Id = data.Id;
			model.Version = data.Version;

			// Sauvegarde du contenu 
			SaveNewsLetterContentToFile(model);

			PrepareNewsletter(model);
			var key = string.Format("NewsLetter.NewsLetter.{0}", model.Id);
			CacheService.Remove(key);
			CacheService.Remove("NewsLetter.NewsLetter.Dictionary");
		}

		private void SaveNewsLetterContentToFile(Models.NewsLetter model)
		{
			int loop = 0;
			var fileName = model.FileName ?? string.Format("nl{0:000000}.cshtml", model.Id);
			var emailContentFileName = System.IO.Path.Combine(GetPhysicalPath(), fileName);
			while (true)
			{
				System.IO.StreamWriter emailfs = null;
				try
				{
					if (System.IO.File.Exists(emailContentFileName))
					{
						System.IO.File.Delete(emailContentFileName);
					}
					using (emailfs = new System.IO.StreamWriter(emailContentFileName, false, System.Text.Encoding.UTF8))
					{
						emailfs.Write(model.Content);
						emailfs.Flush();
						emailfs.Close();
					}

					break;
				}
				catch (Exception ex)
				{
					Logger.Error(ex);
				}
				finally
				{
					if (emailfs != null)
					{
						emailfs.Close();
						emailfs.Dispose();
					}
				}
				loop++;
				if (loop > 5)
				{
					System.Threading.Thread.Sleep(200);
					break;
				}
			}
		}

		public Dictionary<int, string> GetNewsLetterDictionary()
		{
			var key = "NewsLetter.NewsLetter.Dictionary";
			var result = CacheService[key] as Dictionary<int, string>;
			if (result != null)
			{
				return result;
			}
			var query = DbRepository.Query<Repositories.Datas.NewsLetterData>().ToDictionary(k => k.Id, v => v.Content);
			result = new Dictionary<int, string>();
			foreach (var item in query.Keys)
			{
				result.Add(item, string.Format("NL{0:000000} ({1})", item,  FindTitle(query[item])));
			}
			CacheService.Add(key, result, DateTime.Now.AddHours(1));
			return result;
		}

		public IEnumerable<Models.NewsLetter> GetNewsLettersList(Models.NewsLetterListFilter filter, out int count)
		{
            var query = DbRepository.Query<Repositories.Datas.NewsLetterData>();
			count = 0;

			if (!filter.Search.IsNullOrTrimmedEmpty())
			{
				query = query.Where(i => i.FileName == filter.Search);
			}

			filter.TotalCount = query.Count();

			if (!filter.SortByPropertyName.IsNullOrTrimmedEmpty())
			{
				if (filter.SortDirection == System.ComponentModel.ListSortDirection.Ascending)
				{
					query = query.OrderBy(filter.SortByPropertyName);
				}
				else
				{
					query = query.OrderByDescending(filter.SortByPropertyName);
				}
			}
			else
			{
				query = query.OrderByDescending(i => i.Id);
			}

			query = query.Skip(filter.PageIndex * filter.PageSize).Take(filter.PageSize);
			
			var result = AutoMapper.Mapper.Map<IEnumerable<Models.NewsLetter>>(query.ToList());
			foreach (var item in result)
			{
				PrepareNewsletter(item);
			}
			return result;
		}

		public bool IsNewsLetterDeletable(int id, out string reason)
		{
			var result = true;
			reason = null;
			var campaignList = DbRepository.Query<Repositories.Datas.CampaignData>(i => i.NewsLetterId == id);
			if (campaignList.Count() > 0)
			{
				reason = string.Format("already referenced by {0} campaign(s), delete not allowed", campaignList.Count());
				result = false;
			}
			return result;
		}

		public void DeleteNewsLetter(int id)
		{
			string reason = null;
			if (!IsEmailListSourceDeletable(id, out reason))
			{
				throw new Exception(reason);
			}

			var data = DbRepository.Get<Repositories.Datas.NewsLetterData>(i => i.Id == id);
			if (data == null)
			{
				throw new System.Data.RowNotInTableException();
			}

			DbRepository.Delete<Repositories.Datas.NewsLetterData>(data);
			// Todo supprimer le fichier ?

			var key = string.Format("NewsLetter.NewsLetter.{0}", data.Id);
			CacheService.Remove(key);
			CacheService.Remove("NewsLetter.NewsLetter.Dictionary");
		}

		#endregion

		#region Campaign

		public Models.Campaign CreateCampaign()
		{
			var campaign = new Models.Campaign();
			var lastCampaign = DbRepository.Query<Repositories.Datas.CampaignData>().ToList().LastOrDefault();
			if (lastCampaign != null)
			{
				campaign.ReplyToEmail = Settings.DefaultReplyToEmail ?? lastCampaign.ReplyToEmail;
				campaign.ReplyToEmailName = Settings.DefaultReplyToEmailName ?? lastCampaign.ReplyToEmailName;
				campaign.SMTPServerId = lastCampaign.SMTPServerId;
			}
			else
			{
				campaign.ReplyToEmail = Settings.DefaultReplyToEmail;
				campaign.ReplyToEmailName = Settings.DefaultReplyToEmailName;
			}
			campaign.CreationDate = DateTime.Now;
			campaign.PublicKey = Guid.NewGuid().ToString();

			return campaign;
		}

		public List<ERPStore.Models.BrokenRule> ValidateCampaign(Models.Campaign campaign)
		{
			var result = ValidatorService.Validate(campaign);
			return result;
		}

		public Models.Campaign SaveCampaign(ERPStore.NewsLetters.Models.Campaign campaign)
		{
			// Validation des informations
			var brokenRules = ValidateCampaign(campaign);
			if (!brokenRules.IsNullOrEmpty())
			{
				throw new ValidationException();
			}

			Repositories.Datas.CampaignData data = null;
			if (campaign.Id == 0)
			{
				data = AutoMapper.Mapper.Map<Repositories.Datas.CampaignData>(campaign);
				DbRepository.Insert<Repositories.Datas.CampaignData>(data);
			}
			else
			{
				data = DbRepository.Get<Repositories.Datas.CampaignData>(i => i.Id == campaign.Id);
				if (data == null)
				{
					throw new System.Data.RowNotInTableException();
				}
				if (!data.Version.SequenceEqual(campaign.Version))
				{
					throw new System.Data.DBConcurrencyException();
				}
				data = AutoMapper.Mapper.Map<Models.Campaign, Repositories.Datas.CampaignData>(campaign, data);
				DbRepository.Update<Repositories.Datas.CampaignData>(data);
			}

			campaign.Id = data.Id;
			campaign.Version = data.Version;

			string key = string.Format("NewsLetter.Campaign.{0}", data.PublicKey);
			CacheService.Remove(key);
			key = string.Format("NewsLetter.Campaign.{0}", data.Id);
			CacheService.Remove(key);

			return campaign;
		}

		public IEnumerable<Models.Campaign> GetCampaignList(Models.CampaignListFilter filter, out int count)
		{
			var query = DbRepository.Query<Repositories.Datas.CampaignData>()
						.Include(t => t.SMTPServer)
						.Include(t => t.EmailListSource)
						.Include(t => t.NewsLetter);

			if (!filter.Search.IsNullOrTrimmedEmpty())
			{
				query = query.Where(i => i.Name.IndexOf(filter.Search) != -1);
			}

			count = query.Count();

			if (!filter.SortByPropertyName.IsNullOrTrimmedEmpty())
			{
				if (filter.SortDirection == System.ComponentModel.ListSortDirection.Ascending)
				{
					query = query.OrderBy(filter.SortByPropertyName);
				}
				else
				{
					query = query.OrderByDescending(filter.SortByPropertyName);
				}
			}
			else
			{
				query = query.OrderByDescending(i => i.Id);
			}

			query = query.Skip(filter.PageIndex * filter.PageSize).Take(filter.PageSize);

			var result = AutoMapper.Mapper.Map<IEnumerable<Models.Campaign>>(query.ToList());
			return result;
		}

        public Models.Campaign GetCampaignById(int campaignId)
        {
			string key = string.Format("NewsLetter.Campaign.{0}", campaignId);
			var result = CacheService[key] as Models.Campaign;
			if (result == null)
			{
				var data = DbRepository.Get<Repositories.Datas.CampaignData>(i => i.Id == campaignId);
				result = AutoMapper.Mapper.Map<Models.Campaign>(data);
				CacheService.Add(key, result, DateTime.Now.AddHours(1));
			}
			return result;
        }

		public Models.Campaign GetCampaignByKey(string campaignKey)
		{
			string key = string.Format("NewsLetter.Campaign.{0}", campaignKey);
			var result = CacheService[key] as Models.Campaign;
			if (result == null)
			{
				var data = DbRepository.Get<Repositories.Datas.CampaignData>(i => campaignKey.Equals(i.PublicKey, StringComparison.InvariantCultureIgnoreCase));
				result = AutoMapper.Mapper.Map<Models.Campaign>(data);
				CacheService.Add(key, result, DateTime.Now.AddHours(1));
			}
			return result;
		}

		public string ScheduleCampaign(int campaignId, DateTime startDate)
		{
			var data = DbRepository.Get<Repositories.Datas.CampaignData>(i => i.Id == campaignId);
			if (data == null)
			{
				throw new System.Data.RowNotInTableException();
			}

			var campaign = AutoMapper.Mapper.Map<ERPStore.NewsLetters.Models.Campaign>(data);
			var settings = ERPStore.GlobalConfiguration.Configuration.DependencyResolver.GetService<Models.Settings>();
			var parameters = new Dictionary<string, object>();
			parameters.Add("StartDate", startDate);
			parameters.Add("Campaign", campaign);
			parameters.Add("Settings", settings);

			var pgId = string.Format("ScheduleCampaign:{0}", campaignId);

			if (CloudSoft.Workflows.ProgressReporterCollector.Get(pgId) != null)
			{
				// Already in process
				Logger.Warn("Campaign process already in progress");
				return pgId;
			}

			var pg = CloudSoft.Workflows.ProgressReporterCollector.GetNew();
			pg.Id = pgId;

			CloudSoft.Workflows.WorkflowQueueUserWorkItem.RunAsync(
				new Workflows.ScheduleCampaign()
				, parameters
				, pg
				, null
				, (ex) =>
				{
					Logger.Error(ex);
				}
			);

			return pg.Id;
		}

		public void CancelScheduledCampaign(int id)
		{
			var pgId = string.Format("ScheduleCampaign:{0}", id);
			var pg = CloudSoft.Workflows.ProgressReporterCollector.Get(pgId);
			if (pg != null)
			{
				pg.CancelRequested = true;
			}

			DbRepository.DeleteAll<Repositories.Datas.PendingItemData>(i => i.CampaignId == id);
		}

		public bool IsCampaignDeletable(int campaignId, out string reason)
		{
			reason = null;
			var q1 = DbRepository.Query<Repositories.Datas.PendingItemData>(i => i.CampaignId == campaignId);
			if (q1.Count() > 0)
			{
				reason = "Campaign referenced by pending emails";
			}
			var q2 = DbRepository.Query<Repositories.Datas.EmailSentData>(i => i.CampaignId == campaignId);
			if (q2.Count() > 0)
			{
				reason = "Campaign referenced by sent emails";
			}
			return reason.IsNullOrEmpty();
		}

		public void DeleteCampaign(int id)
		{
			string reason = null;
			if (!IsCampaignDeletable(id, out reason))
			{
				throw new System.Data.ConstraintException(reason);
			}

			var data = DbRepository.Get<Repositories.Datas.CampaignData>(i => i.Id == id);
			if (data == null)
			{
				throw new System.Data.RowNotInTableException();
			}
			DbRepository.Delete<Repositories.Datas.CampaignData>(data);
			string key = string.Format("NewsLetter.Campaign.{0}", data.PublicKey);
			CacheService.Remove(key);
			key = string.Format("NewsLetter.Campaign.{0}", data.Id);
			CacheService.Remove(key);
		}

		public Models.CampaignStat GetCampaignStatByCampaignId(int campaignId)
		{
			var data = DbRepository.Get<Repositories.Datas.CampaignStatData>(i => i.CampaignStatId == campaignId);
			return AutoMapper.Mapper.Map<Models.CampaignStat>(data);
		}

		public void SaveCampaignStat(Models.CampaignStat campaignStat)
		{
			if (campaignStat.CampaignStatId == 0)
			{
				throw new Exception("Campaign must exists");
			}

			var data = DbRepository.Get<Repositories.Datas.CampaignStatData>(i => i.CampaignStatId == campaignStat.CampaignStatId);
			if (data == null)
			{
				data = new Repositories.Datas.CampaignStatData();
				data.CampaignStatId = campaignStat.CampaignStatId;
				DbRepository.Insert<Repositories.Datas.CampaignStatData>(data);
			}
			data = AutoMapper.Mapper.Map<Models.CampaignStat, Repositories.Datas.CampaignStatData>(campaignStat, data);
			DbRepository.Update<Repositories.Datas.CampaignStatData>(data);
		}

		#endregion

		#region PendingItem

		public IList<ERPStore.Models.BrokenRule> ValidatePendingItem(Models.PendingItem model)
		{
			var br = ValidatorService.Validate(model);
			return br;
		}

		public void SavePendingItem(Models.PendingItem pendingItem)
        {
			var br = ValidatePendingItem(pendingItem);
			if (br.Count() > 0)
			{
				throw new ERPStore.Models.ValidationException(br);
			}

			var data = AutoMapper.Mapper.Map<Repositories.Datas.PendingItemData>(pendingItem);
			DbRepository.Insert<Repositories.Datas.PendingItemData>(data);
			pendingItem.Id = data.Id;
        }

		public Models.PendingItem GetPendingItemByCampaignIdAndRecipientId(int campaignId, int emailRecipientId)
		{
			var data = DbRepository.Get<Repositories.Datas.PendingItemData>(i => i.CampaignId == campaignId && i.EmailRecipientId == emailRecipientId);
			return AutoMapper.Mapper.Map<Models.PendingItem>(data);
		}

		public IList<Models.PendingItem> GetPendingItemList(int pageSize)
		{
			var list = DbRepository.Query<Repositories.Datas.PendingItemData>()
				.Where(i => i.ScheduledStartDate <= DateTime.Now)
				.OrderBy(i => i.ScheduledStartDate)
				.Take(pageSize);

			return  AutoMapper.Mapper.Map<IList<Models.PendingItem>>(list);
		}

		public void DeletePendingRequestItem(Models.PendingItem pendingRequestItem)
		{
			var data = DbRepository.Get<Repositories.Datas.PendingItemData>(i => i.Id == pendingRequestItem.Id);
			if (data != null)
			{
				DbRepository.Delete<Repositories.Datas.PendingItemData>(data);
			}
		}

		public void SendMessageWithWebApi(SMTPSettings smtpServer, ERPStore.Models.MailMessage message)
		{
			var client = new System.Net.Http.HttpClient();
			client.DefaultRequestHeaders.Accept.Add(new System.Net.Http.Headers.MediaTypeWithQualityHeaderValue("application/json"));
			client.BaseAddress = new Uri(smtpServer.HostName);
			
			var content = new System.Net.Http.StringContent(message.ToJSon(), Encoding.UTF8, "application/json");
			client.PostAsync("api/emailer/sendmessage", content).Wait(1000 * smtpServer.Timeout);
		}

		public string ForceSendPendingItems()
		{
			var parameters = new Dictionary<string, object>();
			parameters.Add("Settings", Settings);

			var pr = CloudSoft.Workflows.ProgressReporterCollector.GetNew();

			CloudSoft.Workflows.WorkflowQueueUserWorkItem.RunAsync(
				new Workflows.EmailSender()
				, parameters
				, pr
				, null
				, (ex) =>
				{
					Logger.Error(ex);
				});

			return pr.Id;

		}

		#endregion

		#region EmailSent

		public void SaveEmailSent(Models.EmailSentResult emailSent)
		{
			var data = AutoMapper.Mapper.Map<Repositories.Datas.EmailSentData>(emailSent);
			DbRepository.Insert<Repositories.Datas.EmailSentData>(data);
			emailSent.Id = data.Id;
		}

		public IList<Models.EmailSentResult> GetEmailSentDetailList(Models.EmailSentListFilter filter)
		{
			var query = DbRepository.Query<Repositories.Datas.EmailSentData>()
							.Include(t => t.EmailRecipient);

			if (filter.CampaignId.HasValue)
			{
				query = query.Where(i => i.CampaignId == filter.CampaignId.Value);
			}

			if (filter.WithClick.HasValue)
			{
				if (filter.WithClick.Value)
				{
					query = query.Where(i => i.ClickCount > 0);
				}
				else
				{
					query = query.Where(i => i.ClickCount == null);
				}
			}

			if (filter.WithOpen.HasValue)
			{
				if (filter.WithOpen.Value)
				{
					query = query.Where(i => i.OpenCount > 0);
				}
				else
				{
					query = query.Where(i => i.OpenCount == null);
				}
			}

			if (filter.WithUnsubscribe.HasValue)
			{
				if (filter.WithUnsubscribe.HasValue)
				{
					query = query.Where(i => i.UnsubscribeCount > 0);
				}
				else
				{
					query = query.Where(i => i.UnsubscribeCount == null);
				}
			}

			if (!filter.SearchTerm.IsNullOrEmpty())
			{
				query = query.Where(i => i.Email.IndexOf(filter.SearchTerm) != -1
									|| filter.SearchTerm.IndexOf(i.EmailRecipient.FullName) != -1
									|| filter.SearchTerm.IndexOf(i.EmailRecipient.CorporateName) != -1);

			}

			filter.TotalCount = query.Count();

			if (!filter.SortByPropertyName.IsNullOrTrimmedEmpty())
			{
				if (filter.SortDirection == System.ComponentModel.ListSortDirection.Ascending)
				{
					query = query.OrderBy(filter.SortByPropertyName);
				}
				else
				{
					query = query.OrderByDescending(filter.SortByPropertyName);
				}
			}
			else
			{
				query = query.OrderByDescending(i => i.Id);
			}

			query = query.Skip(filter.PageIndex * filter.PageSize).Take(filter.PageSize);

			return AutoMapper.Mapper.Map<IList<Models.EmailSentResult>>(query.ToList());
		}

		public Models.EmailSentResult GetEmailSentById(int emailSentId)
		{
			var result = DbRepository.Get<Repositories.Datas.EmailSentData>(i => i.Id == emailSentId);
			return AutoMapper.Mapper.Map<Models.EmailSentResult>(result);
		}

		public Models.EmailSentResult GetLastEmailSentByRecipientId(int recipientId)
		{
			var list = DbRepository.Query<Repositories.Datas.EmailSentData>(i => i.EmailRecipientId == recipientId).OrderByDescending(i => i.SentDate);

			var data = list.FirstOrDefault();
			return AutoMapper.Mapper.Map<Models.EmailSentResult>(data);
		}

		public Models.EmailSentResult GetLastEmailSent(Models.TrackingKey track)
		{
			var list = DbRepository.Query<Repositories.Datas.EmailSentData>(i => i.EmailRecipientId == track.EmailRecipientId 
					&& i.CampaignId == track.CampaignId 
					&& i.NewsLetterId == track.NewsLetterId).OrderByDescending(i => i.SentDate);

			var data = list.FirstOrDefault();
			return AutoMapper.Mapper.Map<Models.EmailSentResult>(data);
		}

		#endregion

		#region EmailRecipient

		public Models.EmailRecipient CreateEmailRecipient()
		{
			var recipient = new Models.EmailRecipient();
			return recipient;
		}

		public Models.EmailRecipient GetEmailRecipientById(int emailRecipientId)
		{
			var result = DbRepository.Get<Repositories.Datas.EmailRecipientData>(i => i.Id == emailRecipientId);
			return AutoMapper.Mapper.Map<Models.EmailRecipient>(result);
		}

		public Models.EmailRecipient GetEmailRecipientByEmail(string email)
		{
			var data = DbRepository.Get<Repositories.Datas.EmailRecipientData>(i => i.Email == email);
			var result = AutoMapper.Mapper.Map<Models.EmailRecipient>(data);
			return result;
		}

		public IEnumerable<Models.EmailRecipient> GetEmailRecipientByEmailList(IEnumerable<string> list)
		{
			var query = DbRepository.Query<Repositories.Datas.EmailRecipientData>(i => list.Contains(i.Email));
			var result = AutoMapper.Mapper.Map<IEnumerable<Models.EmailRecipient>>(query.ToList());
			return result;
		}

		public IList<ERPStore.Models.BrokenRule> ValidateEmailRecipient(Models.EmailRecipient model)
		{
			var br = ValidatorService.Validate(model);
			return br;
		}

		public void SaveEmailRecipient(EmailRecipient emailRecipient)
		{
			var br = ValidateEmailRecipient(emailRecipient);
			if (br.Count > 0)
			{
				throw new ERPStore.Models.ValidationException(br);
			}

			Repositories.Datas.EmailRecipientData data = null;
			if (emailRecipient.Id == 0)
			{
				data = AutoMapper.Mapper.Map<Repositories.Datas.EmailRecipientData>(emailRecipient);
				DbRepository.Insert<Repositories.Datas.EmailRecipientData>(data);
			}
			else
			{
				data = DbRepository.Get<Repositories.Datas.EmailRecipientData>(i => i.Id == emailRecipient.Id);
				if (data == null)
				{
					throw new System.Data.RowNotInTableException();
				}
				data = AutoMapper.Mapper.Map<Models.EmailRecipient, Repositories.Datas.EmailRecipientData>(emailRecipient, data);
				DbRepository.Update<Repositories.Datas.EmailRecipientData>(data);
			}

			emailRecipient.Id = data.Id;
		}

		public IEnumerable<Models.EmailRecipient> GetEmailRecipientListByEmailListSource(int sourceId, int pageIndex, int pageSize, out int count)
		{
			var list = from er in DbRepository.Query<Repositories.Datas.EmailRecipientBySourceListSourceData>()
						   .Include(t => t.EmailRecipient)
					   where er.EmailListSourceId == sourceId
					   orderby er.EmailRecipient.Id
					   select er.EmailRecipient;

			count = list.Count();
			list = list.Skip(pageIndex * pageSize).Take(pageSize);

			return AutoMapper.Mapper.Map<IEnumerable<Models.EmailRecipient>>(list);
		}


		#endregion

		#region Unsubscribe

		public void Unsubscribe(string email, int newsLetterId)
		{
			var unsubscribe = DbRepository.Get<Repositories.Datas.UnsubscriptionData>(i => i.Email == email);
			int uid = 0;
			if (unsubscribe == null)
			{
				var data = new Repositories.Datas.UnsubscriptionData();
				data.Email = email;
				data.CreationDate = DateTime.Now;
				data.NewsLetterId = newsLetterId;
				DbRepository.Insert<Repositories.Datas.UnsubscriptionData>(data);
				uid = data.Id;
			}

			var emailRecipient = DbRepository.Get<Repositories.Datas.EmailRecipientData>(i => i.Email == email);
			if (emailRecipient != null)
			{
				emailRecipient.UnsubscriptionId = uid;
				DbRepository.Update<Repositories.Datas.EmailRecipientData>(emailRecipient);
			}
		}

		#endregion

		#region SMTPServer

		public IList<Models.SMTPSettings> GetSMTPServerList()
		{
			var list = DbRepository.Query<Repositories.Datas.SMTPServerData>();
			return AutoMapper.Mapper.Map<IList<Models.SMTPSettings>>(list);
		}

		public Models.SMTPSettings GetSMTPServerById(int smtpServerId)
		{
			var key = string.Format("NewsLetter.SMTPServer.{0}", smtpServerId);
			var result = CacheService[key] as Models.SMTPSettings;
			if (result == null)
			{
				var data = DbRepository.Get<Repositories.Datas.SMTPServerData>(i => i.Id == smtpServerId);
				result = AutoMapper.Mapper.Map<Models.SMTPSettings>(data);
				CacheService.Add(key, result, DateTime.Now.AddHours(1));
			}
			return result;
		}

		public Models.SMTPSettings GetSMTPServerByName(string name)
		{
			var data = DbRepository.Get<Repositories.Datas.SMTPServerData>(i => i.Name == name);
			var result = AutoMapper.Mapper.Map<Models.SMTPSettings>(data);
			return result;
		}

		public Models.SMTPSettings CreateSMTPServer()
		{
			var result = new Models.SMTPSettings();
			result.Port = 25;
			result.HostName = "127.0.0.1";

			return result;
		}

		public IList<ERPStore.Models.BrokenRule> ValidateSMTPServer(Models.SMTPSettings model)
		{
			var result = ValidatorService.Validate(model);
			return result;
		}

		public void SaveSMTPServer(Models.SMTPSettings model)
		{
			var validation = ValidateSMTPServer(model);
			if (!validation.IsNullOrEmpty())
			{
				throw new ERPStore.Models.ValidationException(validation.ToList());
			}

			Repositories.Datas.SMTPServerData data = null;
			if (model.Id == 0)
			{
				data = AutoMapper.Mapper.Map<Repositories.Datas.SMTPServerData>(model);
				DbRepository.Insert<Repositories.Datas.SMTPServerData>(data);
			}
			else
			{
				data = DbRepository.Get<Repositories.Datas.SMTPServerData>(i => i.Id == model.Id);
				if (data == null)
				{
					throw new System.Data.RowNotInTableException();
				}
				data = AutoMapper.Mapper.Map<Models.SMTPSettings, Repositories.Datas.SMTPServerData>(model, data);
				DbRepository.Update<Repositories.Datas.SMTPServerData>(data);
			}

			model.Id = data.Id;

			var key = string.Format("NewsLetter.SMTPServer.{0}", model.Id);
			CacheService.Remove(key);
		}

		public bool IsSMTPServerDeletable(int smtpServerId, out string reason)
		{
			reason = null;
			var referenceList = DbRepository.Query<Repositories.Datas.CampaignData>().Where(i => i.SMTPServerId == smtpServerId).ToList();
			if (!referenceList.IsNullOrEmpty())
			{
				reason = string.Format("already referenced by {0} campaign(s), delete not allowed", referenceList.Count);
			}
			return referenceList.IsNullOrEmpty();
		}

		public void DeleteSMTPServer(int smtpServerId)
		{
			string reason = null;
			if (!IsSMTPServerDeletable(smtpServerId, out reason))
			{
				throw new System.Data.ConstraintException(reason);
			}

			var data = DbRepository.Get<Repositories.Datas.SMTPServerData>(i => i.Id == smtpServerId);
			if (data == null)
			{
				throw new System.Data.RowNotInTableException();
			}
			DbRepository.Delete<Repositories.Datas.SMTPServerData>(data);
			var key = string.Format("NewsLetter.SMTPServer.{0}", data.Id);
			CacheService.Remove(key);
		}

		#endregion

		#region EmailListSource

		public IDictionary<string, string> GetEmailListSourceTypeList()
		{
			var services = ERPStore.GlobalConfiguration.Configuration.DependencyResolver.GetServices<IEmailListSourceProvider>();
			return services.ToDictionary(k => k.SourceTypeId, v => v.DisplayName);
		}

		public IEmailListSourceProvider GetEmailListProvider(Campaign campaign)
		{
			var source = GetEmailListSourceById(campaign.EmailListSourceId);
			return GetEmailListProvider(source);
		}

		public IEmailListSourceProvider GetEmailListProvider(Models.EmailListSource source)
		{
			var result = GetEmailListProviderById(source.SourceTypeId);
			result.Initialize(source);
			return result;
		}

		public IEmailListSourceProvider GetEmailListProviderById(string id)
		{
			if (id.IsNullOrTrimmedEmpty())
			{
				return null;
			}

			var services = ERPStore.GlobalConfiguration.Configuration.DependencyResolver.GetServices<IEmailListSourceProvider>();
			var provider = services.SingleOrDefault(i => i.SourceTypeId == id);
			return provider;
		}

		public Models.EmailListSource CreateEmailListSource(string emailSourceTypeId)
		{
			var provider = GetEmailListProviderById(emailSourceTypeId);
			var result = new EmailListSource();
			result.SourceTypeId = emailSourceTypeId;
			provider.Initialize(result);
			result.Settings = provider.GetSerializedSettings();
			return result;
		}

		public IList<ERPStore.Models.BrokenRule> ValidateEmailListSource(Models.EmailListSource model)
		{
			if (model == null)
			{
				throw new ArgumentException("model does not be null");
			}
			var result = new List<ERPStore.Models.BrokenRule>();
			var br1 = ValidatorService.Validate(model);
			if (br1 != null)
			{
				result.AddRange(br1);
			}
			var provider = GetEmailListProvider(model);
			if (provider != null)
			{
				var br2 = provider.Validate();
				if (br2 != null)
				{
					result.AddRange(br2);
				}
			}
			return result;
		}

		public void SaveEmailListSource(Models.EmailListSource model)
		{
			if (model == null)
			{
				throw new ArgumentException("model does not be null");
			}

			var br = ValidateEmailListSource(model);
			if (!br.IsNullOrEmpty())
			{
				throw new ERPStore.Models.ValidationException(br.ToList());
			}

			Repositories.Datas.EmailListSourceData data = null;
			if (model.Id == 0)
			{
				data = AutoMapper.Mapper.Map<Repositories.Datas.EmailListSourceData>(model);
				DbRepository.Insert<Repositories.Datas.EmailListSourceData>(data);
			}
			else
			{
				data = DbRepository.Get<Repositories.Datas.EmailListSourceData>(i => i.Id == model.Id);
				if (data == null)
				{
					throw new System.Data.RowNotInTableException();
				}
				data = AutoMapper.Mapper.Map<Models.EmailListSource, Repositories.Datas.EmailListSourceData>(model, data);
				DbRepository.Update(data);
			}

			model.Id = data.Id;
		}

		public Models.EmailListSource GetEmailListSourceById(int id)
		{
			var data = DbRepository.Get<Repositories.Datas.EmailListSourceData>(i => i.Id == id);
			return AutoMapper.Mapper.Map<Models.EmailListSource>(data);
		}

		public IList<Models.EmailListSource> GetEmailListSourceList()
		{
			var list = DbRepository.Query<Repositories.Datas.EmailListSourceData>();
			return AutoMapper.Mapper.Map<IList<Models.EmailListSource>>(list);
		}

		public IDictionary<int, string> GetEmailListSourceDictionary()
		{
			var dic = DbRepository.Query<Repositories.Datas.EmailListSourceData>().ToDictionary(k => k.Id, v => v.Name);
			return dic;
		}

		public bool IsEmailListSourceDeletable(int id, out string reason)
		{
			reason = null;
			var referenceList = DbRepository.Query<Repositories.Datas.CampaignData>().Where(i => i.EmailListSourceId == id).ToList();
			if (!referenceList.IsNullOrEmpty())
			{
				reason = string.Format("already referenced by {0} campaign(s), delete not allowed", referenceList.Count);
			}
			return referenceList.IsNullOrEmpty();
		}

		public void DeleteEmailListSource(int id)
		{
			string reason = null;
			if (!IsEmailListSourceDeletable(id, out reason))
			{
				throw new System.Data.ConstraintException(reason);
			}

			var data = DbRepository.Get<Repositories.Datas.EmailListSourceData>(i => i.Id == id);
			if (data == null)
			{
				throw new System.Data.RowNotInTableException();
			}

			DbRepository.Delete<Repositories.Datas.EmailListSourceData>(data);
		}

		public void AppendEmailRecipientListToEmailListSource(int id, IEnumerable<Models.EmailRecipient> list)
		{
			using (var transaction = ERPStore.TransactionHelper.GetNewReadCommitted())
			{
				foreach (var item in list)
				{
					var br = ValidateEmailRecipient(item);
					if (!br.IsNullOrEmpty())
					{
						throw new ERPStore.Models.ValidationException(br);
					}
					var data = (from er in DbRepository.Query<Repositories.Datas.EmailRecipientData>()
												.Include(t => t.EmailRecipientBySourceListSourceList)
									where er.Email == item.Email
									select er).SingleOrDefault();

					// Création de l'email 
					if (data == null)
					{
						data = AutoMapper.Mapper.Map<Repositories.Datas.EmailRecipientData>(item);
						DbRepository.Insert(data);
						item.Id = data.Id;
					}
					else // Mise à jour
					{
						var emailid = data.Id;
						data = AutoMapper.Mapper.Map<Models.EmailRecipient, Repositories.Datas.EmailRecipientData>(item, data);
						data.Id = emailid;
						DbRepository.Update(data);
					}

					// 
					if (data.EmailRecipientBySourceListSourceList.IsNullOrEmpty()
							|| !data.EmailRecipientBySourceListSourceList.Any(i => i.EmailRecipientId == data.Id && i.EmailListSourceId == id))
					{
						var join = new Repositories.Datas.EmailRecipientBySourceListSourceData();
						join.EmailListSourceId = id;
						join.EmailRecipientId = data.Id;
						DbRepository.Insert(join);
					}
				}
				transaction.Complete();
			}
		}

		#endregion

		protected string GetPhysicalPath()
		{
			var virtualFilePath = ERPStore.NewsLetters.Configuration.ConfigurationSettings.AppSettings["virtualFilePath"];
			var physicalPath = ERPStore.GlobalConfiguration.Configuration.Settings.PhysicalPath;
			physicalPath = System.IO.Path.Combine(physicalPath, virtualFilePath.Replace("~/", "").Replace("/", @"\"));
			return physicalPath;
		}

		public List<ERPStore.Models.User> GetUserListByFilter(ERPStore.Models.UserListFilter filter, int pageIndex, int pageSize, out int count)
		{
			var query = DbRepository.Query<Repositories.Datas.UserData>();

			if (filter.HasEmail.GetValueOrDefault(true))
			{
				query = query.Where(i => i.Email != null
									&& i.Email != string.Empty
									&& i.Email.IndexOf("@") != -1);
			}
			if (filter.IsCashCustomer.GetValueOrDefault(false))
			{
				query = query.Where(i => (i.IsCashCustomerPerson.HasValue
											&& i.IsCashCustomerPerson.Value));
			}
			if (!filter.Search.IsNullOrEmpty())
			{
				query = query.Where(i => filter.Search.IndexOf(i.Email) != -1
									|| filter.Search.IndexOf(i.FirstName) != -1
									|| filter.Search.IndexOf(i.LastName) != -1);
			}
			if (filter.UserCategoryId.HasValue)
			{
				query = query.Where(i => i.CustomerCategoryId.HasValue
									&& i.CustomerCategoryId.Value == filter.UserCategoryId.Value);
			}
			if (!filter.AgentList.IsNullOrEmpty())
			{
				query = query.Where(i => (filter.AgentList.Contains(i.CustomerVendorId.Value) || filter.AgentList.Contains(i.CashCustomerVendorId.Value)));
			}
			if (filter.SelectionId.HasValue)
			{
				query = query.Include(t => t.EntitySelectionItemList);
				query = query.Where(i => i.EntitySelectionItemList.Any(s => s.EntitySelectionId == filter.SelectionId.Value));
			}

			query =  query.OrderBy(i => i.Id);
			count = query.Count();
			var pagedlist = query.Skip(pageIndex * pageSize).Take(pageSize);

			Logger.Info(pagedlist.ToString());

			return AutoMapper.Mapper.Map<List<ERPStore.Models.User>>(pagedlist.ToList());
		}

	}
}
